import re
import pandas as pd
from rank_bm25 import BM25Okapi
import jieba


def preprocess_text(text):
    """预处理文本：移除无关特殊字符、统一小写并使用jieba分词"""
    # 保留关键符号（角度、公差、单位等）和文字，移除其他特殊字符
    text = re.sub(r'[^\w\s°′±≤·~,：()（）]', '', str(text))
    # 使用jieba进行分词
    words = jieba.cut(text.lower())
    # 过滤空字符串，避免无意义词汇干扰
    return [word for word in words if word.strip()]

def extract_final_inspection_texts(final_inspection, core_fields):
    """从final_inspection提取核心字段，生成文本块与identity映射"""
    text_identity_list = []
    for item in final_inspection:
        # 提取核心字段值，字段不存在时用空字符串填充
        field_values = [item['properties'].get(field, '') for field in core_fields]
        # 拼接为单个文本块（字段间用空格分隔，便于后续分词匹配）
        combined_text = ' '.join([str(val) for val in field_values])
        # 存储（文本块, identity）对
        text_identity_list.append((combined_text, item['identity']))
    return text_identity_list

def bm25_match_inspections(new_df, final_inspection, top_n=1):
    """
    BM25匹配新图纸与老图纸（final_inspection）
    :param new_df: 新图纸的pandas DataFrame（输入df）
    :param final_inspection: 老图纸检验数据列表
    :param top_n: 每个新图纸记录匹配的最相似老图纸数量（默认1，即最相似项）
    :return: dict - {identity: 新图纸对应行的JSON字符串}
    """
    # 1. 定义核心匹配字段（与需求一致）
    core_fields = ["原始分类", "检验项目名称", "检测面", "设计值技术指标"]
    
    # 2. 处理老图纸：生成文本块与identity的映射
    old_texts_with_id = extract_final_inspection_texts(final_inspection, core_fields)
    old_texts = [item[0] for item in old_texts_with_id]  # 老图纸所有文本块
    old_identities = [item[1] for item in old_texts_with_id]  # 对应identity列表
    
    # 3. 预处理老图纸文本块，构建BM25语料库
    tokenized_old_corpus = [preprocess_text(text) for text in old_texts]
    bm25_model = BM25Okapi(tokenized_old_corpus)  # 初始化BM25模型
    
    # 4. 处理新图纸：逐行匹配最相似老图纸
    match_result = {}
    for idx, new_row in new_df.iterrows():

        # 4.1 拼接新图纸当前行的核心字段为文本块
        new_row_values = [new_row[field] for field in core_fields]
        new_text = ' '.join([str(val) for val in new_row_values])
        print(f'🔄 处理新图纸的终检项: {new_row_values}')
        
        # 4.2 预处理新图纸文本，计算与所有老图纸的BM25分数
        tokenized_new_text = preprocess_text(new_text)
        similarity_scores = bm25_model.get_scores(tokenized_new_text)
        
        # 4.3 按分数降序排序，取前N个最相似老图纸的索引
        sorted_indices = similarity_scores.argsort()[::-1][:top_n]
        
        # 4.4 存储匹配结果：identity为键，新图纸行JSON为值（确保无索引干扰）
        for idx_old in sorted_indices:
            old_identity = old_identities[idx_old]
            old_text = old_texts[idx_old]
            print(f' - ✅ 找到最相似的老图纸项: {old_text} (分数: {similarity_scores[idx_old]:.4f})\n')
            new_row_dict = new_row.to_dict()  # 先转为字典
            match_result[old_identity] = new_row_dict  #  # 若同一identity匹配多个新行，保留分数最高的（此处因top_n=1，直接赋值）
     

    # 构建新的终检的格式
    final_inspection_dict = []
    for item in match_result:
        identity = item
        properties = match_result[item]
        # 构建新的终检项
        final_inspection_dict.append({
            "identity": identity,
            "labels": ["终检"],
            "properties": properties
        })
    
    return final_inspection_dict

